#include "Acceptor.h"
#include "Logger.h"
#include "InetAddress.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>

// 创建非阻塞socket fd
static int createNonblocking()
{
    int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
    if (sockfd < 0)
    {
        LOG_FATAL("%s:%s:%d listen socket create err:%d \n", __FILE__, __FUNCTION__, __LINE__, errno);
    }
    return sockfd;
}

Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool resueport)
    :loop_(loop)
    ,acceptSocket_(createNonblocking())       // 创建非阻塞listen fd
    ,acceptChannel_(loop,acceptSocket_.fd())  // 绑定listenfd到channel
    ,listenning_(false)
{
    acceptSocket_.setReuseAddr(true);      // 允许端口复用
    acceptSocket_.setReusePort(true);      // 允许多进程/线程复用端口
    acceptSocket_.bindAddress(listenAddr); // 绑定监听地址

    // TcpServer::start（） Acceptor.listen() 如果有新用户连接，要执行一个回调（connfd => channel => subLoop）
    // baseLoop => acceptChannel_(listenfd) => Acceptor::handleRead()

    // 绑定读事件回调，新连接到来时触发handleRead
    acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));
}

Acceptor::~Acceptor()
{
    acceptChannel_.disableAll();  // 禁用所有事件
    acceptChannel_.remove();      // 从Poller中删除channel
}

void Acceptor::listen()
{
    listenning_ = true;
    acceptSocket_.listen();         // 启动listen，开始监听连接请求
    acceptChannel_.enableReading(); // acceptChannel_ => Poller 将listenfd的读事件注册到EventLoop的Poller上
}

// 当Poller检测到listenfd有读事件发生，即就是有新用户连接了，触发此回调
void Acceptor::handleRead()
{
    InetAddress peerAddr;
    int connfd = acceptSocket_.accept(&peerAddr);  // 接收新连接
    if (connfd >= 0)
    {
        if (newConnectionCallback_)  // 若上层设置了回调，传递connfd和新连接的客户端地址
        {
            newConnectionCallback_(connfd, peerAddr); // 回调要做的事情：轮询找到subLoop，唤醒，分发当前的新客户端的Channel
        }
        else
        {
            ::close(connfd);
        }
    }
    else
    {
        LOG_ERROR("%s:%s:%d accept err:%d \n", __FILE__, __FUNCTION__, __LINE__, errno);

        if (errno == EMFILE)  // 进程打开的文件描述符上限
        {
            LOG_ERROR("%s:%s:%d sockfd reached limit! \n", __FILE__, __FUNCTION__, __LINE__);
        }
    }
}